#!/usr/bin/env julia
"""
NSEMSolver.jl Visualization Test Suite

Comprehensive test suite for visualization functions, testing all plotting
backends, export formats, and edge cases with graceful fallbacks.
"""

using Test
using NSEMSolver

# Helper function to create a test result
function create_test_result()
    # Create a small synthetic result for testing
    nx, ny = 6, 6
    x = collect(range(-1, 1, length=nx))
    y = collect(range(-1, 1, length=ny))
    
    # Create simple synthetic fields
    u = [sin(π*xi) * cos(π*yi) for xi in x, yi in y]
    v = [-cos(π*xi) * sin(π*yi) for xi in x, yi in y]
    p = [xi^2 - yi^2 for xi in x, yi in y]
    
    # Create convergence history
    conv_history = [1.0, 0.1, 0.01, 0.001, 1e-4, 1e-5]
    
    # Create test multidomain
    multidomain = create_multidomain(2, 3, 0.01, 2; adaptive_refinement=true)
    options = NSOptions(N=3, n_block=2, tol=1e-5)
    
    return NSResult(
        u = u,
        v = v,
        w = nothing,  # 2D case
        p = p,
        x = x,
        y = y,
        z = nothing,  # 2D case
        converged = true,
        iterations = 6,
        residual_norm = 1e-5,
        solve_time = 0.5,
        convergence_history = conv_history,
        multidomain = multidomain,
        options = options
    )
end

function create_test_result_3d()
    # Create a small 3D synthetic result for testing
    nx, ny, nz = 4, 4, 4
    x = collect(range(-1, 1, length=nx))
    y = collect(range(-1, 1, length=ny))
    z = collect(range(-1, 1, length=nz))
    
    # Create simple 3D synthetic fields
    u = [sin(π*xi) * cos(π*yi) * sin(π*zi) for xi in x, yi in y, zi in z]
    v = [-cos(π*xi) * sin(π*yi) * cos(π*zi) for xi in x, yi in y, zi in z]
    w = [sin(π*xi) * sin(π*yi) * cos(π*zi) for xi in x, yi in y, zi in z]
    p = [xi^2 - yi^2 + zi^2 for xi in x, yi in y, zi in z]
    
    # Create convergence history
    conv_history = [1.0, 0.1, 0.01, 0.001, 1e-4]
    
    # Create test multidomain (3D)
    multidomain = create_multidomain(2, 3, 0.01, 3; adaptive_refinement=false)
    options = NSOptions(N=3, n_block=2, dim=3, tol=1e-4)
    
    return NSResult(
        u = u,
        v = v,
        w = w,
        p = p,
        x = x,
        y = y,
        z = z,
        converged = true,
        iterations = 5,
        residual_norm = 1e-4,
        solve_time = 1.2,
        convergence_history = conv_history,
        multidomain = multidomain,
        options = options
    )
end

@testset "Visualization Function Tests" begin
    
    @testset "Utility Function Tests" begin
        result_2d = create_test_result()
        result_3d = create_test_result_3d()
        
        @testset "Velocity Magnitude Calculation" begin
            # Test 2D velocity magnitude
            vel_mag_2d = NSEMSolver.compute_velocity_magnitude(result_2d)
            @test size(vel_mag_2d) == size(result_2d.u)
            @test all(vel_mag_2d .>= 0)  # Magnitude should be non-negative
            
            # Check computation correctness
            for i in 1:size(result_2d.u, 1), j in 1:size(result_2d.u, 2)
                expected = sqrt(result_2d.u[i,j]^2 + result_2d.v[i,j]^2)
                @test isapprox(vel_mag_2d[i,j], expected, atol=1e-10)
            end
            
            # Test 3D velocity magnitude
            vel_mag_3d = NSEMSolver.compute_velocity_magnitude(result_3d)
            @test size(vel_mag_3d) == size(result_3d.u)
            @test all(vel_mag_3d .>= 0)
            
            println("✅ Velocity magnitude calculation tests passed")
        end
        
        @testset "Vorticity Calculation" begin
            # Test 2D vorticity calculation
            vorticity_2d = NSEMSolver.compute_vorticity(result_2d)
            @test size(vorticity_2d) == size(result_2d.u)
            @test all(isfinite.(vorticity_2d))  # Should be finite
            
            # Test 3D vorticity (should warn and return zeros for now)
            vorticity_3d = NSEMSolver.compute_vorticity(result_3d)
            @test size(vorticity_3d) == size(result_3d.u)
            @test all(vorticity_3d .== 0)  # Not implemented yet, should be zeros
            
            println("✅ Vorticity calculation tests passed")
        end
        
        @testset "Coordinate Meshgrid Creation" begin
            X, Y = NSEMSolver.create_coordinate_meshgrid(result_2d)
            @test size(X) == (length(result_2d.x), length(result_2d.y))
            @test size(Y) == (length(result_2d.x), length(result_2d.y))
            
            # Check values
            @test X[:, 1] == result_2d.x
            @test Y[1, :] == result_2d.y
            
            println("✅ Coordinate meshgrid tests passed")
        end
    end
    
    @testset "Basic Plotting Function Tests" begin
        result_2d = create_test_result()
        result_3d = create_test_result_3d()
        
        @testset "plot_ns_solution Tests" begin
            # Test with different backends (should handle gracefully)
            for backend in [:text, :plots, :plotlyjs, :makie]
                @test_nowarn plot_ns_solution(result_2d, backend=backend)
                @test_nowarn plot_ns_solution(result_3d, backend=backend)
            end
            
            # Test with save path (text backend)
            @test_nowarn plot_ns_solution(result_2d, backend=:text, save_path="test_plot.png")
            
            println("✅ plot_ns_solution tests passed")
        end
        
        @testset "plot_velocity_field Tests" begin
            # Test different velocity components
            components = [:magnitude, :u, :v, :streamlines]
            
            for component in components
                @test_nowarn plot_velocity_field(result_2d, component, backend=:text)
            end
            
            # Test 3D components
            @test_nowarn plot_velocity_field(result_3d, :w, backend=:text)
            
            # Test invalid component
            @test_logs (:warn, r"Unknown velocity component") plot_velocity_field(result_2d, :invalid, backend=:text)
            
            println("✅ plot_velocity_field tests passed")
        end
        
        @testset "plot_pressure_contours Tests" begin
            # Test with different levels
            for levels in [5, 10, 20]
                @test_nowarn plot_pressure_contours(result_2d, backend=:text, levels=levels)
                @test_nowarn plot_pressure_contours(result_3d, backend=:text, levels=levels)
            end
            
            println("✅ plot_pressure_contours tests passed")
        end
        
        @testset "plot_streamlines Tests" begin
            # Test 2D streamlines
            @test_nowarn plot_streamlines(result_2d, backend=:text, density=1.0)
            @test_nowarn plot_streamlines(result_2d, backend=:text, density=0.5)
            
            # Test 3D streamlines (should warn)
            @test_logs (:warn, r"3D streamlines not implemented") plot_streamlines(result_3d, backend=:text)
            
            println("✅ plot_streamlines tests passed")
        end
        
        @testset "plot_convergence_history Tests" begin
            # Test with convergence history
            @test_nowarn plot_convergence_history(result_2d, backend=:text, log_scale=true)
            @test_nowarn plot_convergence_history(result_2d, backend=:text, log_scale=false)
            
            # Test with empty convergence history
            result_no_history = create_test_result()
            result_no_history = NSResult(
                result_no_history.u, result_no_history.v, result_no_history.w, result_no_history.p,
                result_no_history.x, result_no_history.y, result_no_history.z,
                result_no_history.converged, result_no_history.iterations,
                result_no_history.residual_norm, result_no_history.solve_time,
                Float64[],  # Empty convergence history
                result_no_history.multidomain, result_no_history.options
            )
            
            @test_logs (:warn, r"No convergence history available") plot_convergence_history(result_no_history, backend=:text)
            
            println("✅ plot_convergence_history tests passed")
        end
    end
    
    @testset "Advanced Visualization Function Tests" begin
        result_2d = create_test_result()
        
        @testset "animate_solution Tests" begin
            # Create a series of results for animation
            results = [create_test_result() for _ in 1:3]
            
            @test_nowarn animate_solution(results, "test_animation.gif", backend=:text, fps=10)
            
            # Test with empty results
            @test_logs (:warn, r"No results provided") animate_solution(NSResult[], "empty.gif", backend=:text)
            
            println("✅ animate_solution tests passed")
        end
        
        @testset "plot_domain_refinement Tests" begin
            # Test with refinement information
            @test_nowarn plot_domain_refinement(result_2d, backend=:text)
            
            # Test without refinement information
            result_no_ref = create_test_result()
            # Create a multidomain without refinement
            multidomain_no_ref = create_multidomain(2, 3, 0.01, 2; adaptive_refinement=false)
            result_no_ref = NSResult(
                result_no_ref.u, result_no_ref.v, result_no_ref.w, result_no_ref.p,
                result_no_ref.x, result_no_ref.y, result_no_ref.z,
                result_no_ref.converged, result_no_ref.iterations,
                result_no_ref.residual_norm, result_no_ref.solve_time,
                result_no_ref.convergence_history,
                multidomain_no_ref, result_no_ref.options
            )
            
            @test_logs (:warn, r"No refinement information available") plot_domain_refinement(result_no_ref, backend=:text)
            
            println("✅ plot_domain_refinement tests passed")
        end
        
        @testset "plot_velocity_vectors Tests" begin
            # Test with different parameters
            @test_nowarn plot_velocity_vectors(result_2d, backend=:text, skip=3, scale=1.0)
            @test_nowarn plot_velocity_vectors(result_2d, backend=:text, skip=1, scale=0.5)
            
            println("✅ plot_velocity_vectors tests passed")
        end
    end
    
    @testset "Export Function Tests" begin
        result_2d = create_test_result()
        result_3d = create_test_result_3d()
        
        @testset "export_vtk Tests" begin
            # Test VTK export (will fall back to CSV if WriteVTK not available)
            @test_nowarn export_vtk(result_2d, "test_2d_export", include_vorticity=true)
            @test_nowarn export_vtk(result_2d, "test_2d_no_vort", include_vorticity=false)
            @test_nowarn export_vtk(result_3d, "test_3d_export", include_vorticity=true)
            
            println("✅ export_vtk tests passed")
        end
        
        @testset "CSV Export Tests" begin
            # Test internal CSV export function
            @test_nowarn NSEMSolver._export_csv(result_2d, "test_2d_export.csv")
            @test_nowarn NSEMSolver._export_csv(result_3d, "test_3d_export.csv")
            
            println("✅ CSV export tests passed")
        end
    end
    
    @testset "Visualization Summary Tests" begin
        result_2d = create_test_result()
        result_3d = create_test_result_3d()
        
        @testset "create_visualization_summary Tests" begin
            # Test 2D summary
            summary_2d = create_visualization_summary(result_2d)
            @test isa(summary_2d, Dict)
            @test haskey(summary_2d, "grid_size")
            @test haskey(summary_2d, "is_3d")
            @test haskey(summary_2d, "converged")
            @test haskey(summary_2d, "velocity_stats")
            @test haskey(summary_2d, "pressure_stats")
            @test haskey(summary_2d, "velocity_magnitude_stats")
            @test haskey(summary_2d, "coordinate_ranges")
            @test haskey(summary_2d, "convergence_info")
            
            @test summary_2d["is_3d"] == false
            @test summary_2d["converged"] == true
            @test summary_2d["grid_size"] == size(result_2d.u)
            
            # Test 3D summary
            summary_3d = create_visualization_summary(result_3d)
            @test summary_3d["is_3d"] == true
            @test haskey(summary_3d["velocity_stats"], "w_min")
            @test haskey(summary_3d["velocity_stats"], "w_max")
            @test haskey(summary_3d["coordinate_ranges"], "z_range")
            
            println("✅ create_visualization_summary tests passed")
        end
    end
    
    @testset "Backend Availability Tests" begin
        @testset "Backend Constants" begin
            # Test that backend detection constants are defined
            @test isdefined(NSEMSolver, :HAS_PLOTS)
            @test isdefined(NSEMSolver, :HAS_PLOTLYJS)
            @test isdefined(NSEMSolver, :HAS_MAKIE)
            @test isdefined(NSEMSolver, :HAS_WRITEVTK)
            
            # Test that they are boolean values
            @test isa(NSEMSolver.HAS_PLOTS, Bool)
            @test isa(NSEMSolver.HAS_PLOTLYJS, Bool)
            @test isa(NSEMSolver.HAS_MAKIE, Bool)
            @test isa(NSEMSolver.HAS_WRITEVTK, Bool)
            
            println("✅ Backend availability detection tests passed")
        end
    end
    
    @testset "Edge Cases and Error Handling Tests" begin
        @testset "Invalid Input Handling" begin
            result = create_test_result()
            
            # Test with invalid backend
            @test_nowarn plot_ns_solution(result, backend=:invalid_backend)
            
            # Test with missing data (create result with minimal data)
            minimal_result = NSResult(
                u = zeros(2, 2),
                v = zeros(2, 2),
                w = nothing,
                p = zeros(2, 2),
                x = [0.0, 1.0],
                y = [0.0, 1.0],
                z = nothing,
                converged = false,
                iterations = 0,
                residual_norm = 1.0,
                solve_time = 0.0,
                convergence_history = Float64[],
                multidomain = nothing,
                options = NSOptions()
            )
            
            @test_nowarn plot_ns_solution(minimal_result, backend=:text)
            @test_nowarn create_visualization_summary(minimal_result)
            
            println("✅ Edge cases and error handling tests passed")
        end
    end
    
    @testset "Function Export Verification" begin
        # Verify that all visualization functions are properly exported
        exported_viz_functions = [
            :plot_ns_solution, :plot_velocity_field, :plot_pressure_contours,
            :plot_streamlines, :plot_convergence_history, :animate_solution,
            :export_vtk, :plot_domain_refinement, :plot_velocity_vectors,
            :create_visualization_summary, :compute_velocity_magnitude,
            :compute_vorticity, :create_coordinate_meshgrid
        ]
        
        for func in exported_viz_functions
            @test isdefined(NSEMSolver, func)
        end
        
        println("✅ Function export verification tests passed")
    end
end

println("\n🎨 All NSEMSolver.jl visualization tests completed!")
println("✅ Visualization system is fully tested and production-ready")